/******************************************************************************
 *
 * MotionViewer
 * Copyright (C) 2007 Sangyoon Lee, Electronic Visualization Laboratory, 
 * University of Illinois at Chicago
 *
 * This software is free software; you can redistribute it and/or modify it 
 * under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation; either Version 2.1 of the License, or 
 * (at your option) any later version.
 *
 * This software is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public 
 * License for more details.
 * 
 * You should have received a copy of the GNU Lesser Public License along
 * with this software; if not, write to the Free Software Foundation, Inc., 
 * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
 *
 * Questions or comments about MotionViewer should be directed to 
 * cavern@evl.uic.edu
 *
 * Original source code from Ogre File Dialog Example
 * Refer to http://www.ogre3d.org/wiki/index.php/CEGUICommonFileDialog
 * Mainly modified for mac platform compatibility
 *****************************************************************************/
#include <ogreplatform.h>
#if OGRE_PLATFORM == OGRE_PLATFORM_APPLE
#include <Carbon/Carbon.h>
#endif

#include "CEGUIHashMapTemplates.h"
#include "CEGUICommonFileDialog.h"
#include <CEGUI/elements/CEGUIFrameWindow.h>
#include <CEGUI/elements/CEGUICombobox.h>

#include <iostream>
#include <stdio.h>

#if OGRE_PLATFORM == OGRE_PLATFORM_WIN32
#include <direct.h>
#include <io.h>
#else
#include <sys/types.h>
#include <sys/dir.h>
#endif

#include <algorithm>
#include <cctype>



namespace CEGUI
{
	/*************************************************************************
		CommonFileDialogInput::Getters & Setters
	*************************************************************************/
	uint CommonFileDialogInput::getHandle (void)
	{
		return _uHandle;
	}

	String CommonFileDialogInput::getFileName (void)
	{
		return _szFileName;
	}

	String CommonFileDialogInput::getFilter (void)
	{
		return _szFilter;
	}

	String CommonFileDialogInput::getDefaultExtension (void)
	{
		return _szDefaultExtension;
	}

	String CommonFileDialogInput::getTitle (void)
	{
		return _szTitle;
	}

	bool CommonFileDialogInput::getOpenFileDialog (void)
	{
		return _bOpenFileDialog;
	}

	bool CommonFileDialogInput::getDoPrompt (void)
	{
		return _bDoPrompt;
	}

	String CommonFileDialogInput::getPromptText (void)
	{
		return _szPromptText;
	}

	void CommonFileDialogInput::setHandle (uint uHandle)
	{
		_uHandle = uHandle;
	}

	void CommonFileDialogInput::setFilter (const String szFilter)
	{
		_szFilter = szFilter;
	}

	void CommonFileDialogInput::setDefaultExtension (const String szDefaultExtension)
	{
		_szDefaultExtension = szDefaultExtension;
	}

	void CommonFileDialogInput::setFileName (const String szFileName)
	{
		_szFileName = szFileName;
	}

	void CommonFileDialogInput::setTitle (const String szTitle)
	{
		_szTitle = szTitle;
	}

	void CommonFileDialogInput::setOpenFileDialog (const bool bOpenFileDialog)
	{
		_bOpenFileDialog = bOpenFileDialog;
	}

	void CommonFileDialogInput::setDoPrompt (const bool bDoPrompt)
	{
		_bDoPrompt = bDoPrompt;
	}

	void CommonFileDialogInput::setPromptText (const String szPromptText)
	{
		_szPromptText = szPromptText;
	}

	/*************************************************************************
		CommonFileDialogOutput::Getters & Setters
	*************************************************************************/
	uint CommonFileDialogOutput::getHandle (void)
	{
		return _uHandle;
	}

	String CommonFileDialogOutput::getFullQualifiedFileName (void)
	{
		return _szFullQualifiedFileName;
	}

	String CommonFileDialogOutput::getDrive (void)
	{
		return _szDrive;
	}

	String CommonFileDialogOutput::getAbsolutePath (void)
	{
		return _szAbsolutePath;
	}

	String CommonFileDialogOutput::getRelativePath (void)
	{
		return _szRelativePath;
	}

	String CommonFileDialogOutput::getFileName (void)
	{
		return _szFileName;
	}

	CommonFileDialogAction CommonFileDialogOutput::getAction (void)
	{
		return _action;
	}

	bool CommonFileDialogOutput::isFileExisting (void)
	{
		return _bFileExisting;
	}

	void CommonFileDialogOutput::setHandle (const uint uHandle)
	{
		_uHandle = uHandle;
	}

	void CommonFileDialogOutput::setFullQualifiedFileName (const String szFullQualifiedFileName)
	{
		_szFullQualifiedFileName = szFullQualifiedFileName;
	}

	void CommonFileDialogOutput::setDrive (const String szDrive)
	{
		_szDrive = szDrive;
	}

	void CommonFileDialogOutput::setAbsolutePath (const String szAbsolutePath)
	{
		_szAbsolutePath = szAbsolutePath;
	}

	void CommonFileDialogOutput::setRelativePath (const String szRelativePath)
	{
		_szRelativePath = szRelativePath;
	}

	void CommonFileDialogOutput::setFileName (const String szFileName)
	{
		_szFileName = szFileName;
	}

	void CommonFileDialogOutput::setAction (CommonFileDialogAction action)
	{
		_action = action;
	}

	void CommonFileDialogOutput::setFileExisting (bool bFileExisting)
	{
		_bFileExisting = bFileExisting;
	}

	/*************************************************************************
		Singleton
	*************************************************************************/
#if OGRE_PLATFORM == OGRE_PLATFORM_WIN32	
	CommonFileDialog::_InstPtr CommonFileDialog::sm_ptr;
#endif

	/*************************************************************************
		CCommonFileDialog::CCommonFileDialog
	*************************************************************************/
	CCommonFileDialog::CCommonFileDialog(void)
	{
		// Load the window layout, initialise the widget attributes and subscribe to events
		_mWindow = WindowManager::getSingleton().loadWindowLayout((utf8*)NAME_LAYOUT_COMMON_FILE_DIALOG.c_str());
		_mBtnBack = (PushButton*)WindowManager::getSingleton().getWindow((utf8*)NAME_PUSHBUTTON_BACK.c_str());
		_mBtnOk = (PushButton*)WindowManager::getSingleton().getWindow((utf8*)NAME_PUSHBUTTON_OK.c_str());
		_mBtnCancel = (PushButton*)WindowManager::getSingleton().getWindow((utf8*)NAME_PUSHBUTTON_CANCEL.c_str());
		_mCmbFile = (Combobox*)WindowManager::getSingleton().getWindow((utf8*)NAME_COMBOBOX_FILE.c_str());
		_mCmbDrives = (Combobox*)WindowManager::getSingleton().getWindow((utf8*)NAME_COMBOBOX_DRIVE.c_str());
		_mCmbExtensions = (Combobox*)WindowManager::getSingleton().getWindow((utf8*)NAME_COMBOBOX_EXTENSION.c_str());
		_mLbxFiles = (Listbox*)WindowManager::getSingleton().getWindow((utf8*)NAME_LISTBOX_FILES.c_str());

		_mWindowPromptLoad = (FrameWindow*)WindowManager::getSingleton().getWindow((utf8*)NAME_FRAMEWINDOW_PROMPT_LOAD.c_str());
		_mStPromptLoad = WindowManager::getSingleton().getWindow((utf8*)NAME_STATICTEXT_PROMPT_LOAD.c_str());
		_mBtnPromptLoadOk = (PushButton*)WindowManager::getSingleton().getWindow((utf8*)NAME_PUSHBUTTON_OK_PROMPT_LOAD.c_str());
		
		_mWindowPromptSave = (FrameWindow*)WindowManager::getSingleton().getWindow((utf8*)NAME_FRAMEWINDOW_PROMPT_SAVE.c_str());
		_mStPromptSave = WindowManager::getSingleton().getWindow((utf8*)NAME_STATICTEXT_PROMPT_SAVE.c_str());
		_mBtnPromptSaveOk = (PushButton*)WindowManager::getSingleton().getWindow((utf8*)NAME_PUSHBUTTON_OK_PROMPT_SAVE.c_str());
		_mBtnPromptSaveCancel = (PushButton*)WindowManager::getSingleton().getWindow((utf8*)NAME_PUSHBUTTON_CANCEL_PROMPT_SAVE.c_str());

		// Add a drive combobox
		_determineDrives ();
		vector<String>::iterator pos;
		for (pos = _drives.begin(); pos < _drives.end(); pos++)
			_mCmbDrives->addItem(new ListboxTextItem (*pos));

		if (_mWindow)
		{
			_mWindow->hide();
			_mWindow->disable();
			_mWindow->subscribeEvent(FrameWindow::EventCloseClicked, Event::Subscriber(&CCommonFileDialog::_handleClose, this));
		}

		if (_mBtnBack)
		{
			_mBtnBack->setText ("<<"); // Must set it here, otherwise the xml parser gets confused
			_mBtnBack->subscribeEvent(PushButton::EventClicked, Event::Subscriber(&CCommonFileDialog::_handleBack, this));
		}

		if (_mBtnCancel)
			_mBtnCancel->subscribeEvent(PushButton::EventClicked, Event::Subscriber(&CCommonFileDialog::_handleCancel, this));

		if (_mBtnOk)
			_mBtnOk->subscribeEvent(PushButton::EventClicked, Event::Subscriber(&CCommonFileDialog::_handleOk, this));

		if (_mCmbExtensions)
			_mCmbExtensions->subscribeEvent(Combobox::EventListSelectionAccepted, Event::Subscriber(&CCommonFileDialog::_handleFilterSelect, this));

		if (_mCmbDrives)
		{
			_mCmbDrives->setText(_getCurrentDrive ());
			_mCmbDrives->subscribeEvent(Combobox::EventListSelectionAccepted, Event::Subscriber(&CCommonFileDialog::_handleDriveSelect, this));
		}

		if (_mLbxFiles)
		{
			_mLbxFiles->setSortingEnabled (true);
			_mLbxFiles->subscribeEvent(Listbox::EventMouseClick, Event::Subscriber(&CCommonFileDialog::_handleFileSelect, this));
			_mLbxFiles->subscribeEvent(Listbox::EventMouseDoubleClick, Event::Subscriber(&CCommonFileDialog::_handleDirectorySelect, this));
		}

		if (_mWindowPromptLoad)
		{
			_mWindowPromptLoad->disable();
			_mWindowPromptLoad->hide();
		}

		if (_mBtnPromptLoadOk)
			_mBtnPromptLoadOk->subscribeEvent(PushButton::EventClicked, Event::Subscriber(&CCommonFileDialog::_handlePromptLoadOk, this));
		
		if (_mWindowPromptSave)
		{
			_mWindowPromptSave->disable();
			_mWindowPromptSave->hide();
		}
		
		if (_mBtnPromptSaveOk)
			_mBtnPromptSaveOk->subscribeEvent(PushButton::EventClicked, Event::Subscriber(&CCommonFileDialog::_handlePromptSaveOk, this));
		
		if (_mBtnPromptSaveCancel)
			_mBtnPromptSaveCancel->subscribeEvent(PushButton::EventClicked, Event::Subscriber(&CCommonFileDialog::_handlePromptSaveCancel, this));
	}

	/*************************************************************************
		CCommonFileDialog::~CCommonFileDialog
		Delete the _Path objects. Use the vector, because iterating through
		the hash_map gives problems
	*************************************************************************/
	CCommonFileDialog::~CCommonFileDialog (void)
	{
		deleteAllEntriesFromHashMap (&_currentPaths);
	}

	/*************************************************************************
		CCommonFileDialog::_disableDialog
		Disables all interaction with the dialog controls
	*************************************************************************/
	void CCommonFileDialog::_disableDialog (void)
	{
		if (_mBtnBack)
			_mBtnBack->disable();
		if (_mBtnOk)
			_mBtnOk->disable();
		if (_mBtnCancel)
			_mBtnCancel->disable();
		if (_mCmbFile)
			_mCmbFile->disable();
		if (_mCmbDrives)
			_mCmbDrives->disable();
		if (_mCmbExtensions)
			_mCmbExtensions->disable();
		if (_mLbxFiles)
			_mLbxFiles->disable();
	}

	/*************************************************************************
		CCommonFileDialog::_enableDialog
		Enables all interaction with the dialog controls
	*************************************************************************/
	void CCommonFileDialog::_enableDialog (void)
	{
		if (_mBtnBack)
			_mBtnBack->enable();
		if (_mBtnOk)
			_mBtnOk->enable();
		if (_mBtnCancel)
			_mBtnCancel->enable();
		if (_mCmbFile)
			_mCmbFile->enable();
		if (_mCmbDrives)
			_mCmbDrives->enable();
		if (_mCmbExtensions)
			_mCmbExtensions->enable();
		if (_mLbxFiles)
			_mLbxFiles->enable();
	}

	/*************************************************************************
		CCommonFileDialog::_closeWindow
		Actually closes the window
	*************************************************************************/
	void CCommonFileDialog::_closeWindow (void)
	{
		if (_mWindow)
		{
			_mWindow->disable();
			_mWindow->hide();
		}

		_closePromptWindowLoad ();
		_closePromptWindowSave ();
	}

	/*************************************************************************
		CCommonFileDialog::_closePromptWindowLoad
	*************************************************************************/
	void CCommonFileDialog::_closePromptWindowLoad (void)
	{
		if (_mWindowPromptLoad)
		{
			_mWindowPromptLoad->disable();
			_mWindowPromptLoad->hide();
		}
		_enableDialog ();
	}

	/*************************************************************************
		CCommonFileDialog::_closePromptWindowSave
	*************************************************************************/
	void CCommonFileDialog::_closePromptWindowSave (void)
	{
		if (_mWindowPromptSave)
		{
			_mWindowPromptSave->disable();
			_mWindowPromptSave->hide();
		}
		_enableDialog ();
	}

	/*************************************************************************
		CCommonFileDialog::_openPromptWindowLoad
		Opens the prompt window in case of loading
	*************************************************************************/
	void CCommonFileDialog::_openPromptWindowLoad (void)
	{
		if (_mWindowPromptLoad)
		{
			_disableDialog();
			_mWindowPromptLoad->show();
			_mWindowPromptLoad->enable();
		}
	}

	/*************************************************************************
		CCommonFileDialog::_openPromptWindowSave
		Opens the prompt window in case of saving
	*************************************************************************/
	void CCommonFileDialog::_openPromptWindowSave (void)
	{
		if (_mWindowPromptSave)
		{
			_disableDialog();
			_mWindowPromptSave->show();
			_mWindowPromptSave->enable();
		}
	}

	/*************************************************************************
		CCommonFileDialog::_handleClose
		Close the window
	*************************************************************************/
	bool CCommonFileDialog::_handleClose (const CEGUI::EventArgs& e)
	{
		_prefillOutputAndClearInput ();
		_currentOutput.setAction (ACTION_WINDOW_CLOSED);
		_closeWindow ();
		return true;
	}

	/*************************************************************************
		CCommonFileDialog::_handleCancel
		Cancel Loading/saving and close the window
	*************************************************************************/
	bool CCommonFileDialog::_handleCancel (const CEGUI::EventArgs& e)
	{
		_prefillOutputAndClearInput ();
		_currentOutput.setAction (ACTION_CANCEL);
		_closeWindow ();
		return true;
	}

	/*************************************************************************
		CCommonFileDialog::_okExecute
	*************************************************************************/
	void CCommonFileDialog::_okExecute (void)
	{
		_prefillOutputAndClearInput ();
		_Path* path = _getPathOfSelectedDrive ();
		if (path)
		{
			_currentOutput.setFullQualifiedFileName (	path->szAbsolutePath + 
														SEPARATOR + 
														_getCurrentFile());
			_currentOutput.setAbsolutePath (path->szAbsolutePath);
			_currentOutput.setRelativePath (path->szRelativePath);
		}
		
		_currentOutput.setFileName (_getCurrentFile());
		_currentOutput.setFileExisting (_isFileExisting(_getCurrentFile()));
		_currentOutput.setAction (ACTION_OK);

		// Add the filename to the combobox (= history)
		_addFileNameToHistory (_getCurrentFile());

		_closeWindow ();
	}

	/*************************************************************************
		CCommonFileDialog::_handleOk
		Executed when Ok has been pressed.
		An output object is created with the current settings or in case 
		prompt has been set, the prompt window is displayed
	*************************************************************************/
	bool CCommonFileDialog::_handleOk (const CEGUI::EventArgs& e)
	{
		if (_currentInput.getDoPrompt() &&
			_currentInput.getOpenFileDialog() &&
			!_isFileExisting(_getCurrentFile()))
		{
				_openPromptWindowLoad();
				return true;
		}
			
		if (_currentInput.getDoPrompt() &&
			!_currentInput.getOpenFileDialog() &&
			_isFileExisting(_getCurrentFile()))
		{
				_openPromptWindowSave();
				return true;
		}

		_okExecute ();
		return true;
	}

	/*************************************************************************
		CCommonFileDialog::_handleBack
		One step back in the directory tree
	*************************************************************************/
	bool CCommonFileDialog::_handleBack (const CEGUI::EventArgs& e)
	{
		_adjustPathOfSelectedDrive (BRACKET_OPEN + ".." + BRACKET_CLOSED);
		_fillListbox ();
		return true;
	}

	/*************************************************************************
		CCommonFileDialog::_handleDriveSelect
		Executed when a drive is selected
	*************************************************************************/
	bool CCommonFileDialog::_handleDriveSelect (const CEGUI::EventArgs& e)
	{
		_fillListbox ();
		return true;
	}

	/*************************************************************************
		CCommonFileDialog::_handleFilterSelect
		Executed when a filter (extension) is selected.
	*************************************************************************/
	bool CCommonFileDialog::_handleFilterSelect (const CEGUI::EventArgs& e)
	{
		_fillListbox ();

		if (!_currentInput.getOpenFileDialog())
		{
			// Dialog is open for 'Save As'. Only then the extension of the file
			// is changed after selecting an extension
			_FilterElement* filterElement = _getCurrentFilterElement();
			if (filterElement)
			{
				// Get the extension without the "*."
				String szExt = _stripFileExtension(filterElement->szExtension);

				// Get the filename without the extension and set it in the combobox
				String szFileName = _stripFileExtension(_getCurrentFile(), false);
				String szRes = szFileName + "." + szExt;
				_setCurrentFile (szRes);
			}
		}

		return true;
	}

	/*************************************************************************
		CCommonFileDialog::_handleFileSelect
		Executed when a filename (in the listbox) is selected
	*************************************************************************/
	bool CCommonFileDialog::_handleFileSelect (const CEGUI::EventArgs& e)
	{
		if (_mLbxFiles)
		{
			ListboxTextItem* item = static_cast<ListboxTextItem*>(_mLbxFiles->getFirstSelectedItem ());
			if (item)
			{
				// Return if a directory is selected
				if (item->getText ().length() > 0)
					if (item->getText ()[0] == BRACKET_OPEN_ASCII)
						return true;

				if (_mCmbFile)
					_mCmbFile->setText(item->getText());
			}
		}
		return true;
	}

	/*************************************************************************
		CCommonFileDialog::_handleDirectorySelect
		Executed when a directory (in the listbox) is selected
	*************************************************************************/
	bool CCommonFileDialog::_handleDirectorySelect (const CEGUI::EventArgs& e)
	{
		if (_mLbxFiles)
		{
			ListboxTextItem* item = static_cast<ListboxTextItem*>(_mLbxFiles->getFirstSelectedItem ());
			if (item)
			{
				// Return if no directory is selected
				if (item->getText ().length() > 0)
					if (item->getText ()[0] != BRACKET_OPEN_ASCII)
						return true;

				// Determine new directory
				_adjustPathOfSelectedDrive (item->getText ());
				_fillListbox ();
			}
		}
		return true;
	}

	/*************************************************************************
		CCommonFileDialog::_handlePromptLoadOk
		Executed when the Ok button in the prompt load window has been pushed
	*************************************************************************/
	bool CCommonFileDialog::_handlePromptLoadOk (const CEGUI::EventArgs& e)
	{
		_closePromptWindowLoad ();
		return true;
	}

		
	/*************************************************************************
		CCommonFileDialog::_handlePromptSaveOk
		Executed when the Ok button in the prompt save window has been pushed
	*************************************************************************/
	bool CCommonFileDialog::_handlePromptSaveOk (const CEGUI::EventArgs& e)
	{
		_okExecute ();
		return true;
	}

	/*************************************************************************
		CCommonFileDialog::_handlePromptSaveCancel
		Executed when the Cancel button in the prompt save window has been pushed
	*************************************************************************/
	bool CCommonFileDialog::_handlePromptSaveCancel (const CEGUI::EventArgs& e)
	{
		_closePromptWindowSave ();
		return true;
	}

	/*************************************************************************
		CCommonFileDialog::_isFileExisting
		Determines whether a file exists in the selected path
	*************************************************************************/
	bool CCommonFileDialog::_isFileExisting (const String szFileName)
	{
		String szPathPattern = _getPathOfSelectedDrive()->szAbsolutePath;

#if OGRE_PLATFORM == OGRE_PLATFORM_WIN32
		szPathPattern = szPathPattern + SEPARATOR + "*";
#else
		szPathPattern = szPathPattern + SEPARATOR;
#endif

		String szName = "";
		bool found = false;

#if OGRE_PLATFORM == OGRE_PLATFORM_WIN32
		struct _finddata_t c_file;
		long hFile;

		if( (hFile = _findfirst(szPathPattern.c_str(), &c_file )) != -1L)
		{
			szName = String(c_file.name);
			if ((!(c_file.attrib & _A_SUBDIR)) && strcmp(c_file.name, szFileName.c_str()) == 0)
				found = true;
			
			// Get the other ones
			while (_findnext( hFile, &c_file ) == 0 && !found)
			{
				szName = String(c_file.name);
				if ((!(c_file.attrib & _A_SUBDIR)) && strcmp(c_file.name, szFileName.c_str()) == 0)
					found = true;
			}
		}
		
		_findclose( hFile );
		
#else
		DIR *dp;
		struct direct *dentry;

		if ((dp=opendir(szPathPattern.c_str())) == NULL)
		{
			std::cout << "\nopendir failed opening path: " << szPathPattern << "\n";
			return false;
		}
		for(dentry=readdir(dp); dentry != NULL; dentry=readdir(dp))
		{
			if(strcmp(dentry->d_name, szFileName.c_str()) == 0)
				found = true;
		}
		
		closedir(dp);
#endif
		
		return found;
	}

	/*************************************************************************
		CCommonFileDialog::_prefillOutputAndClearInput
		Fills the output object with generic data and clear the input data
	*************************************************************************/
	void CCommonFileDialog::_prefillOutputAndClearInput (void)
	{
		_currentOutput.setHandle (_currentInput.getHandle());
		_currentOutput.setFullQualifiedFileName ("");
		_currentOutput.setDrive (_getCurrentDrive());
		_currentOutput.setAbsolutePath ("");
		_currentOutput.setRelativePath ("");
		_currentOutput.setFileName ("");

		_currentInput.setDefaultExtension ("");
		_currentInput.setFileName ("");
		_currentInput.setFilter ("");
		_currentInput.setHandle (0);
		_currentInput.setTitle ("");
	}

	/*************************************************************************
		CCommonFileDialog::_determineDrives
		Determine all active drives
	*************************************************************************/
	void CCommonFileDialog::_determineDrives (void)
	{
		_drives.clear();

#if OGRE_PLATFORM == OGRE_PLATFORM_WIN32
		String szDrive = "A:";
		ulong uDriveMask = _getdrives();
		if (uDriveMask != 0)
		{
			while (uDriveMask)
			{
				if (uDriveMask & 1)
					_drives.push_back (szDrive);

				++szDrive[0];
				uDriveMask >>= 1;
			}
		}
#else
		// root directory
		String mac_root = "/";
		_drives.push_back(mac_root);
		
		// home directory
		char *home;
		String mac_home = "/home";
		if ((home = getenv("HOME")) != NULL)
			mac_home = home;
		_drives.push_back(mac_home);
		
		// media directory
		String mac_media = "/Volume";
		_drives.push_back(mac_media);
#endif
	}

	/*************************************************************************
		CCommonFileDialog::_stripBrackets
		Strip the left and right bracket of a string
	*************************************************************************/
	String CCommonFileDialog::_stripBrackets(const String szItem)
	{
		String szNewItem = "";
		if (szItem.length() > 1)
		{
			szNewItem.assign (szItem, 1, szItem.length() - 2);
		}
		return szNewItem;
	}

	/*************************************************************************
		CCommonFileDialog::_stripDrive
		Returns the drive of a full qualified path
	*************************************************************************/
	String CCommonFileDialog::_stripDrive(const String szItem)
	{
		String szDrive;
		if (szItem.length() > 1)
		{
			return szDrive.assign (szItem, 0, 2);
		}
		else
		{
			return "";
		}
	}

	/*************************************************************************
		CCommonFileDialog::_stripFileExtension
		Strips the extension of a file and returns either:
		- The extension (returnRightPart = true; the default)
		- The filename without extension (returnRightPart = false)
	*************************************************************************/
	String CCommonFileDialog::_stripFileExtension(const String szItem, bool bReturnRightPart)
	{
		if (szItem.length() == 0)
			return "";

		String szExt;
		String::size_type pos = szItem.rfind (".", szItem.length());
		if (pos != string::npos)
		{
			if (bReturnRightPart)
			{
				pos++;
				return szExt.assign (szItem, pos, szItem.length() - pos);
			}
			else
			{
				return szExt.assign (szItem, 0, pos);
			}
		}
		
		return "";
	}


	/*************************************************************************
		CCommonFileDialog::_matchItem (String)
		Determines whether an item (directory or filename) matches the
		selected filter element.
	*************************************************************************/
	bool CCommonFileDialog::_matchItem (const String szItem)
	{
		// If the item doesn't have an extension, false is returned
		if (szItem.length() == 0)
			return false;

		// Return true if it is a directory
		if (szItem.length() > 0)
			if (szItem[0] == BRACKET_OPEN_ASCII)
				return true;

		// Return true if all files should be displayed
		_FilterElement* filterElement = _getCurrentFilterElement();
		if (filterElement)
		{
			if (filterElement->szExtension == EXTENSION_ALL)
				return true;

			// Do they match?
			String szExt = "*." + _stripFileExtension (szItem);
			// james mod for not case sensitive
			std::string lower(szExt.c_str());
			std::string upper(szExt.c_str());

#if OGRE_PLATFORM == OGRE_PLATFORM_WIN32
			std::transform(lower.begin(), lower.end(), lower.begin(), std::tolower);
			std::transform(upper.begin(), upper.end(), upper.begin(), std::toupper);
#else
			std::transform(lower.begin(),lower.end(), lower.begin(), (int(*)(int))std::tolower);
			std::transform(upper.begin(), upper.end(), upper.begin(), (int(*)(int))std::toupper);
#endif

			if (lower == filterElement->szExtension || upper == filterElement->szExtension)
				return true;
		}
	
		return false;
	}

	/*************************************************************************
		CCommonFileDialog::_getWorkingPath
		Get the current working directory (full qualified)
	*************************************************************************/
	String CCommonFileDialog::_getWorkingPath (void)
	{
#if OGRE_PLATFORM == OGRE_PLATFORM_WIN32
		char currentPath[_MAX_PATH];
		if ( _getcwd(currentPath, _MAX_PATH ) != NULL )
		{
			return currentPath;
		}
#else
		char *currentPath;
		if ( (currentPath = getenv("PWD")) != NULL)
		{
			return currentPath;
		}
#endif
		return "";
	}

	/*************************************************************************
		CCommonFileDialog::_getCurrentDrive
		Get the current drive. This can be:
		- The selected drive
		- If no drive selected, the drive of the working directory is returned
	*************************************************************************/
	String CCommonFileDialog::_getCurrentDrive (void)
	{
		// Return the selected drive (if available)
		if (_mCmbDrives)
		{
			ListboxTextItem* item = static_cast <ListboxTextItem*>(_mCmbDrives->getSelectedItem());
			if (item)
			{
				return item->getText();
			}
		}

		// If no drive was selected, use the one from the working directory
#if OGRE_PLATFORM == OGRE_PLATFORM_WIN32
		return _stripDrive (_getWorkingPath());
#else
		return _getWorkingPath();
#endif
	}

	/*************************************************************************
		CCommonFileDialog::_getCurrentFilterElement
		Get the current filter element. This can be:
		- The selected one from the extensions combobox
		- If no one selected, the first one from the list is returned
	*************************************************************************/
	CCommonFileDialog::_FilterElement* CCommonFileDialog::_getCurrentFilterElement (void)
	{
		// Return the selected one  (if available)
		if (_mCmbExtensions)
		{
			_FilterElement* item = static_cast <_FilterElement*>(_mCmbExtensions->getSelectedItem());
			if (item)
			{
				return item;
			}

			// If no one was selected, use the one which is presented
			String szText = _mCmbExtensions->getText ();
			uint uNumberOfEntries = _mCmbExtensions->getItemCount();
			for (uint uPos = 0; uPos < uNumberOfEntries; uPos++)
			{
				_FilterElement* item = static_cast <_FilterElement*>(_mCmbExtensions->getListboxItemFromIndex (uPos));
				if (item)
				{
					if (szText == item->szDescription)
						return item;
				}
			}
			
			// If nothing displayed, use the first from the list
			if (uNumberOfEntries)
			{
				_FilterElement* item = static_cast <_FilterElement*>(_mCmbExtensions->getListboxItemFromIndex (0));
				if (item)
					return item;
			}
		}

		return NULL;
	}
	
	/*************************************************************************
		CCommonFileDialog::_setCurrentFile
		Set a filename in the files combobox.
	*************************************************************************/
	void CCommonFileDialog::_setCurrentFile (const String szNewFileName)
	{
		if (_mCmbFile)
		{
			// Change the currently displayed text
			_mCmbFile->setText(szNewFileName);
		}
	}

	/*************************************************************************
		CCommonFileDialog::_getCurrentFile
		Get the filename from the combobox. If no filename selected, the
		currently displayed one is returned.
	*************************************************************************/
	String CCommonFileDialog::_getCurrentFile (void)
	{
		// Return the selected file from the combobox (if available)
		if (_mCmbFile)
		{
			return _mCmbFile->getText();
		}

		return "";
	}


	/*************************************************************************
		CCommonFileDialog::_addFileNameToHistory
		Insert the filename to the filename combobox (at the beginning), which
		serves as a history of all loaded and saved files. If the combobox
		contains too many items, the last one will be removed.
		Note, that the combobox currently only contains filenames, without
		path.
	*************************************************************************/
	void CCommonFileDialog::_addFileNameToHistory (const String szFileName)
	{
		_mCmbFile->insertItem (new ListboxTextItem (szFileName.c_str()), NULL);
		uint uNumberOfItems = _mCmbFile->getItemCount ();
		if (uNumberOfItems > MAX_NUMBER_OF_HISTORY_ITEMS)
			_mCmbFile->removeItem (_mCmbFile->getListboxItemFromIndex (--uNumberOfItems));
	}

	/*************************************************************************
		CCommonFileDialog::_adjustPathOfSelectedDrive
		Changes the directory of the selected drive.
	*************************************************************************/
	void CCommonFileDialog::_adjustPathOfSelectedDrive (const String szDirectoryChange)
	{
		// Set absolute path
		_Path* path = _getPathOfSelectedDrive ();
		String szDir = _stripBrackets (szDirectoryChange);
		if (szDir == "..")
		{
			// Go one step back
			String::size_type pos = path->szAbsolutePath.rfind (SEPARATOR, path->szAbsolutePath.length());
			if (pos != string::npos)
			{
				path->szAbsolutePath.assign (path->szAbsolutePath, 0, pos);
			}
		}
		else
		{
			if (szDir == ".")
			{
				// Go to the root
				path->szAbsolutePath = _getCurrentDrive();
			}
			else
			{
				// Must be a directory step, update the path
				path->szAbsolutePath = path->szAbsolutePath + SEPARATOR + szDir;
			}
		}

		// Set relative path
		// Only in case the drive of the working path is identical to the drive of the absolute path
		String szDrive = _getCurrentDrive();
		String szCurrentPath = _getWorkingPath();
		if (szDrive == _stripDrive(szCurrentPath))
		{
			path->szRelativePath = "";
			String szBranchWorkingPath = "";
			String szBranchAbsolutePath = "";
			String szRelative = "";
			uint uLenCurrent = szCurrentPath.length();
			uint uLenAbs = path->szAbsolutePath.length();
			uint u = 0;

			// Determine where the the working path and the absolute path are branching
			while (szCurrentPath[u] == path->szAbsolutePath[u] && u < uLenCurrent && u < uLenAbs)
				u++;

			// Go back until the first separator is detected
			if (u < uLenCurrent)
				while (szCurrentPath[u] != SEPARATOR[0] && u >= 0)
					u--;

			// Get the branch of the working path
			szBranchWorkingPath.assign (szCurrentPath, u, szCurrentPath.length() - u);
	
			// Get the branch of the absolute path (increase counter by 1 to prevent duplicate separators)
			if (u < uLenAbs)
				u++;
			szBranchAbsolutePath.assign (path->szAbsolutePath, u, path->szAbsolutePath.length() - u);

			// The amount of separators determines the steps back.
			uint uLenBranchWorkingPath = szBranchWorkingPath.length();
			for (u = 0; u < uLenBranchWorkingPath; u++)
			{
				if (szBranchWorkingPath[u] == SEPARATOR[0])
					szRelative = szRelative + ".." + SEPARATOR;
			}

			// Concatenate the 2 parts
			path->szRelativePath = szRelative + szBranchAbsolutePath;
		}
	}


	/*************************************************************************
		CCommonFileDialog::_getPathOfSelectedDrive
		Returns the path of the selected drive.
	*************************************************************************/
	CCommonFileDialog::_Path* CCommonFileDialog::_getPathOfSelectedDrive (void)
	{
		String szDrive = _getCurrentDrive();
		_Path* path = getEntryFromHashMap(string(szDrive.c_str()), &_currentPaths);
		if (!path)
		{
			// No path found, add a new one
			path = new _Path ("");
			String szCurrentPath = _getWorkingPath();
			if (szDrive == _stripDrive(szCurrentPath))
				path->szAbsolutePath = szCurrentPath;
			else
				path->szAbsolutePath = szDrive;
		
			addEntryToHashMap (string(szDrive.c_str()), path, &_currentPaths);
		}
		
		return path;
	}

	/*************************************************************************
		CCommonFileDialog::_fillListbox
		Fill the listbox with files
	*************************************************************************/
	void CCommonFileDialog::_fillListbox  (void)
	{
		String szName;
		String szPathPattern;

		if (_mLbxFiles)
		{
			szPathPattern = _getPathOfSelectedDrive()->szAbsolutePath;
			_mLbxFiles->resetList();
			
#if OGRE_PLATFORM == OGRE_PLATFORM_WIN32
			szPathPattern = szPathPattern + SEPARATOR + "*";
			
			struct _finddata_t c_file;
			long hFile;
			
			if( (hFile = _findfirst( szPathPattern.c_str(), &c_file )) == -1L)
			{
				// Nothing in directory
			}
			else
			{
				szName = String(c_file.name);
				if (c_file.attrib & _A_SUBDIR)
					szName = BRACKET_OPEN + szName + BRACKET_CLOSED;
				
				// Add it to the listbox if it passes the filter
				if (_matchItem(szName))
					_mLbxFiles->addItem(new ListboxTextItem((utf8*)szName.c_str()));
			}
			
			// Get the other ones
			while( _findnext( hFile, &c_file ) == 0 )
			{
				szName = String(c_file.name);
				if (c_file.attrib & _A_SUBDIR)
					szName = BRACKET_OPEN + szName + BRACKET_CLOSED;
				
				// Add it to the listbox if it passes the filter
				if (_matchItem(szName))
					_mLbxFiles->addItem(new ListboxTextItem((utf8*)szName.c_str()));
			}
			
			_findclose( hFile );
#else
			szPathPattern = szPathPattern + SEPARATOR;
			
			DIR *dp;
			struct direct *dentry;
			
			if ( (dp=opendir(szPathPattern.c_str())) == NULL)
			{
				std::cout << "\nopendir failedopening path: " << szPathPattern << "\n";
				exit(-1);
			}
			
			if ((dentry = readdir(dp)) == NULL)
			{
				std::cout << "\nNothing in directory\n";
			}
			else
			{
				szName = String(dentry->d_name);
				if (dentry->d_type == DT_DIR)
					szName = BRACKET_OPEN + szName + BRACKET_CLOSED;
				
				// Add it to the listbox if it passes the filter
				if (_matchItem(szName))
					_mLbxFiles->addItem(new ListboxTextItem((utf8*)szName.c_str()));
			}
			
			// Get the other ones
			while((dentry=readdir(dp)) != NULL)
			{
				szName = String(dentry->d_name);
				
				if (dentry->d_type == DT_DIR)
					szName = BRACKET_OPEN + szName + BRACKET_CLOSED;
				
				if (_matchItem(szName))
					_mLbxFiles->addItem(new ListboxTextItem((utf8*)szName.c_str()));
			}
			
			closedir(dp);
#endif

		}
	}


	/*************************************************************************
		CCommonFileDialog::openDialog
		Call the file dialog
	*************************************************************************/
	void CCommonFileDialog::openDialog (CommonFileDialogInput& input)
	{
		if (_mWindow)
		{
			// Store the input
			bool bDefaultExtensionSet = false;
			_currentInput = input;

			// Parse the filter and fill the filter elements (extensions) in the combobox
			_filterParser.parseFilter (input.getFilter());
			vector<_Filter*> filter = _filterParser.getParsedFilter();
			if (_mCmbExtensions && filter.size() > 0)
			{
				// Loop through the filter and add the elements to the combobox
				_mCmbExtensions->resetList ();
				vector<CCommonFileDialog::_Filter*>::iterator pos;
				for (pos = filter.begin(); pos < filter.end(); pos++)
				{
					if (*pos)
					{
						_mCmbExtensions->addItem(new _FilterElement ((*pos)->szDescription, (*pos)->szExtension));

						// Was the default extension part of the element. If yes, then show it
						if (input.getDefaultExtension() == (*pos)->szExtension)
						{
							_mCmbExtensions->setText((*pos)->szDescription);
							bDefaultExtensionSet = true;
						}
					}
				}

				// If the default extension in the input object isn't available; set the first in the list
				if (!bDefaultExtensionSet)
				{
					_FilterElement* filterElement = _getCurrentFilterElement ();
					if (filterElement)
						_mCmbExtensions->setText(filterElement->szDescription);
				}
	
				// Fill the list
				_fillListbox ();
			}

			// Set the title
			if (input.getTitle() != "")
			{
				_mWindow->setText((CEGUI::utf8*)input.getTitle().c_str());
				if (_currentInput.getOpenFileDialog())
					_mWindowPromptLoad->setText((CEGUI::utf8*)input.getTitle().c_str());
				else
					_mWindowPromptSave->setText((CEGUI::utf8*)input.getTitle().c_str());
			}
			else
			{
				if (_currentInput.getOpenFileDialog())
				{
					_mWindowPromptLoad->setText((CEGUI::utf8*)DEFAULT_TEXT_DIALOG_LOAD.c_str());
					_mWindow->setText((CEGUI::utf8*)DEFAULT_TEXT_DIALOG_LOAD.c_str());
				}
				else
				{
					_mWindowPromptSave->setText((CEGUI::utf8*)DEFAULT_TEXT_DIALOG_SAVE.c_str());
					_mWindow->setText((CEGUI::utf8*)DEFAULT_TEXT_DIALOG_SAVE.c_str());
				}
			}

			// Set the text in the prompt window
			if (input.getDoPrompt())
			{
				if (input.getPromptText() != "")
				{
					if (_currentInput.getOpenFileDialog())
						_mStPromptLoad->setText((CEGUI::utf8*)input.getPromptText().c_str());
					else
						_mStPromptSave->setText((CEGUI::utf8*)input.getPromptText().c_str());
				}
				else
				{
					if (_currentInput.getOpenFileDialog())
						_mStPromptLoad->setText((CEGUI::utf8*)DEFAULT_TEXT_PROMPT_LOAD.c_str());
					else
						_mStPromptSave->setText((CEGUI::utf8*)DEFAULT_TEXT_PROMPT_SAVE.c_str());
				}
			}

			// Display the (default) filename
			_mCmbFile->setText((CEGUI::utf8*)input.getFileName().c_str());

			// Show the dialog
			_mWindow->show();
			_mWindow->enable();
			_enableDialog();
		}
	}

	/*************************************************************************
		CCommonFileDialog::getResult
		Returns the output object after the Ok button has been pressed
	*************************************************************************/
	CommonFileDialogOutput CCommonFileDialog::getResult (void)
	{
		return _currentOutput;
	}

	/*************************************************************************
		CCommonFileDialog::_FilterParser::__deleteFilter
		Delete the vector with filter elements
	*************************************************************************/
	void CCommonFileDialog::_FilterParser::__deleteFilter()
	{
		vector<_Filter*>::iterator pos;
		for (pos = __filter.begin(); pos < __filter.end(); pos++)
		{
			if (*pos)
				delete *pos;
		}

		__filter.clear();
	}

	/*************************************************************************
		CCommonFileDialog::_FilterParser::parseFilter
		Parse the filter string. The string is divided and the parsed data
		is added to _Filter objects
	*************************************************************************/
	void CCommonFileDialog::_FilterParser::parseFilter(const String szFilter)
	{
		__deleteFilter ();
		_Filter* filter;
		String szPart;
		bool push = false;
		String::size_type idxStart = 0;
		String::size_type idxEnd = szFilter.find ("|", idxStart);
		while (idxEnd != String::npos)
		{
			szPart.assign (szFilter, idxStart, idxEnd - idxStart);
			if (!push)
			{
				filter = new _Filter();
				if (filter)
					filter->szDescription = szPart;
				push = true;
			}
			else
			{
				if (filter)
				{
					filter->szExtension = szPart;
					__filter.push_back (filter);
					push = false;
				}
			}

			idxStart = ++idxEnd;
			idxEnd = szFilter.find ("|", idxStart);
		}
	}

	/*************************************************************************
		CCommonFileDialog::_FilterParser::getParsedFilter
		Return the parsed filter as a vector with Filter pointers
	*************************************************************************/
	vector<CCommonFileDialog::_Filter*> CCommonFileDialog::_FilterParser::getParsedFilter(void)
	{
		return __filter;
	}
}

